using System;
using System.Collections;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace SharpMud.DynamicTyping
{
	/// <summary>
	/// AssembliedTypeLocation is a descendant of TypeLocation that supports targeting a type
	/// that is in an assembly stored in a file.
	/// </summary>
	[XmlRoot("AssembliedType")]
	public class AssembliedTypeLocation : DynamicTyping.TypeLocation
	{
		private string _AssemblyLocation;

		/// <summary>
		/// The default constructor for the AssembliedType class.
		/// </summary>
		public AssembliedTypeLocation()
		{
		}

		/// <summary>
		/// A constructor of the AssembliedType class.
		/// </summary>
		/// <param name="assemblyLocation">Either an absolute path or a relative path to the assembly the type is located in.</param>
		/// <param name="fullTypeName">The full name of the target System.Type</param>
		public AssembliedTypeLocation(string assemblyLocation, string fullTypeName)
		{
			this._AssemblyLocation = assemblyLocation;
			this.FullTypeName = fullTypeName;
		}

		/// <summary>
		/// Either an absolute path or a relative path to the assembly the type is located in.
		/// </summary>
		[XmlElement("AssemblyLocation")]
		public string AssemblyLocation
		{
			get
			{
				return _AssemblyLocation;
			}
			set
			{
				_AssemblyLocation = value;
			}
		}

		/// <summary>
		/// This method loads the target assembly from its file and returns it.
		/// </summary>
		/// <returns>Returns the loaded target assembly</returns>
		public override System.Reflection.Assembly AcquireAssembly()
		{
			if(System.IO.Path.IsPathRooted(this.AssemblyLocation))
			{
				return System.Reflection.Assembly.LoadFile(this.AssemblyLocation);	
			}
			else
			{
				string p = System.Environment.CurrentDirectory;
				string fp = System.IO.Path.Combine(p, this.AssemblyLocation);
				
				if(System.IO.Path.GetFileName(fp).ToLower().Trim()=="system.dll")
					return (typeof(Uri)).Assembly;
				if(System.IO.Path.GetFileName(fp).ToLower().Trim()=="mscorlib.dll")
					return (typeof(int)).Assembly;
				if(System.IO.Path.GetFileName(fp).ToLower().Trim()=="system.xml.dll")
					return (typeof(System.Xml.XmlDocument)).Assembly;
				if(System.IO.Path.GetFileName(fp).ToLower().Trim()=="system.data.dll")
					return (typeof(System.Data.DataSet)).Assembly;
				if(System.IO.Path.GetFileName(fp).ToLower().Trim()=="microsoft.visualbasic.dll")
					return (typeof(Microsoft.VisualBasic.ControlChars)).Assembly;

                System.Reflection.Assembly ass;
                try
                {
                    ass = System.Reflection.Assembly.LoadFile(fp);
                }
                catch (System.IO.FileNotFoundException e)
                {
                    throw new AssemblyNotFoundException("The assembly [" + this.AssemblyLocation + "] could not be found.", e);
                }
                catch (System.IO.DirectoryNotFoundException e)
                {
                    throw new AssemblyNotFoundException("The assembly [" + this.AssemblyLocation + "] could not be found.", e);
                }
                return ass;
			}
		}

		/// <summary>
		/// Gets this AssembliedType's target type from its target assembly
		/// </summary>
		/// <returns></returns>
		public override Type AcquireType()
		{
			System.Reflection.Assembly ass;
			System.Type					type;
			ass = AcquireAssembly();
			if(ass==null)
				throw new AssemblyNotFoundException("The assembly [" + this.AssemblyLocation + "] could not be found.");
			type = ass.GetType(this.FullTypeName,true,false);
			if(type==null)
				throw new TypeNotFoundException("The type (" + this.FullTypeName + ") could not be found in assembly [" + this.AssemblyLocation + "].");
			return type;
		}

		/// <summary>
		/// Determines and returns a value that indicates whether or not the target type exists within the
		/// target assembly
		/// </summary>
		public bool TypeExists
		{
			get
			{
				if(!this.AssemblyExists) 
					return false;
				else if(this.AcquireType()!=null)
					return true;
				else
					return false;
			}
		}

		/// <summary>
		/// Determines and returns a value that indicates whether or not the target assembly exists
		/// </summary>
		public bool AssemblyExists
		{
			get
			{
				return System.IO.File.Exists(this.AssemblyLocation);
			}
		}

		/// <summary>
		/// Creates a new instance of an AssembliedType that points at an already loaded type
		/// </summary>
		/// <param name="loadedType">The loaded type that this AssembliedType will point to</param>
		/// <returns>Returns an AssembliedType pointing at the given loaded type</returns>
		public static AssembliedTypeLocation FromLoadedType(System.Type loadedType)
		{
			AssembliedTypeLocation AT = new AssembliedTypeLocation(loadedType.Assembly.Location,loadedType.FullName);
			return AT;
		}
	}
}